<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <script type="text/javascript" src="sylvester.js"></script>
    <script type="text/javascript" src="glUtils.js"></script>

    <script id="shader-fs" type="x-shader/x-fragment">
      varying vec4 vColor;

      uniform float uAlpha;

      void main(void){
        //gl_FragColor = vec4(0.0,1.0,1.0,6.0);
        //gl_FragColor = vec4(vColor.rgb,vColor.a * 0.8);
        gl_FragColor = vec4(vColor.rgb,vColor.a );
      }
    </script>
    
    <script id="shader-vs" type="x-shader/x-vertex">
      attribute vec3 aVertexPosition;
      attribute vec4 aVertexColor;

      uniform mat4 uMVMatrix;
      uniform mat4 uPMatrix;

      varying vec4 vColor;

      void main(void){
        gl_Position= uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
        vColor = aVertexColor;
      }
    </script>

<script type="text/javascript">

var rangeScale=50;
var rangeX=50;
var rangeY=50;
var rangeZ=50;
var keyUp=0;
var keyRight=0;

var currentRing = new Object();
var ringStack = [];

var gl;
function initGL(canvas){
try{
    gl = canvas.getContext("experimental-webgl");
  }catch(e){
  }
  if(!gl){
    alert("このブラウザでWebGLは利用出来なさそうです。ごめんなさい。");
  }
}

function getShader(gl,id){
  var shaderScript = document.getElementById(id);
  if(!shaderScript){
    return null;
  }

  var str = "";
  var k = shaderScript.firstChild;
  while(k){
    if(k.nodeType == 3){
      str += k.textContent;
    }
    k = k.nextSibling;
  }

  var shader;
  if(shaderScript.type == "x-shader/x-fragment"){
    shader = gl.createShader(gl.FRAGMENT_SHADER);
  }else if( shaderScript.type == "x-shader/x-vertex"){
    shader = gl.createShader(gl.VERTEX_SHADER);
  }else{
    return null;
  }

  gl.shaderSource(shader, str);
  gl.compileShader(shader);

  if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS)){
    alert(gl.getShaderInfoLog(shader));
    return null;
  }
  return shader;
}

var shaderProgram;
function initShaders(){
  var fragmentShader = getShader(gl, "shader-fs");
  var vertexShader = getShader(gl,"shader-vs");

  shaderProgram = gl.createProgram();
  gl.attachShader(shaderProgram,vertexShader);
  gl.attachShader(shaderProgram, fragmentShader);
  gl.linkProgram(shaderProgram);

  if(!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)){
    alert("シェーダの初期化失敗");
  }

  gl.useProgram(shaderProgram);

  shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
  gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

  shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram,"aVertexColor");
  gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);

  shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram,"uPMatrix");
  shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram,"uMVMatrix");

  shaderProgram.alphaUniform = gl.getUniformLocation(shaderProgram,"uAlpha");
}
var mvMatrix;
function loadIdentity(){
  mvMatrix = Matrix.I(4);
}
function multMatrix(m){
  mvMatrix = mvMatrix.x(m);
}
function mvTranslate(v){
  var m = Matrix.Translation($V([v[0],v[1],v[2]])).ensure4x4();
  multMatrix(m);
}
function mvRotate(ang,v){
  var arad = ang * Math.PI / 180.0;
  var m = Matrix.Rotation(arad, $V([v[0],v[1],v[2]])).ensure4x4();
  multMatrix(m);
}

var mvMatrixStack = [];
function mvPushMatrix(m){
  if(m){
    mvMatrixStack.push(m.dup());
    mvMatrix = m.dup();
  }else{
    mvMatrixStack.push(mvMatrix.dup());
  }
}

function mvPopMatrix(){
  if(mvMatrixStack.length == 0){
    throw "Invalid popMatrix!";
  }
  mvMatrix = mvMatrixStack.pop();
  return mvMatrix;
}

var pMatrix;
function perspective(fovy,aspect,znear,zfar){
  pMatrix = makePerspective(fovy,aspect,znear,zfar);
}

function setMatrixUniforms(){
  gl.uniformMatrix4fv(shaderProgram.pMatrixUniform,false,new WebGLFloatArray(pMatrix.flatten()));
  gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform,false,new WebGLFloatArray(mvMatrix.flatten()));
}

var pyramidVertexPositionBuffer;
var pyramidVertexColorBuffer;
var cubeVertexPositionBuffer;
var cubeVertexColorBuffer;
var cubeVertexIndexBuffer;

function initBuffers(){
  pyramidVertexPositionBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER,pyramidVertexPositionBuffer);
  var vertices =[
    //Front
    0.0,  1.0,   0.0,
    -1.0, -1.0,  1.0,
    1.0,  -1.0,  1.0,

    //Right
    0.0,  1.0,   0.0,
    1.0, -1.0,  1.0,
    1.0, -1.0,  -1.0,

    //Back
    0.0,  1.0,   0.0,
    1.0, -1.0,  -1.0,
    -1.0,  -1.0,  -1.0,

    //Left
    0.0,  1.0,   0.0,
    -1.0, -1.0,  -1.0,
    -1.0,  -1.0,  1.0,
   ];
  gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(vertices),gl.STATIC_DRAW);
  pyramidVertexPositionBuffer.itemSize = 3;
  pyramidVertexPositionBuffer.numItems = 12;

  pyramidVertexColorBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexColorBuffer);
  var colors =[
    //Front
    1.0,  0.0,  0.0,  0.0,
    0.0,  1.0,  0.0,  1.0,
    0.0,  0.0,  1.0,  1.0,
    //Right
    1.0,  0.0,  0.0,  0.0,
    0.0,  0.0,  1.0,  1.0,
    0.0,  1.0,  0.0,  1.0,
    //Back
    1.0,  0.0,  0.0,  0.0,
    0.0,  1.0,  0.0,  1.0,
    0.0,  0.0,  1.0,  1.0,
    //Left
    1.0,  0.0,  0.0,  0.0,
    0.0,  0.0,  1.0,  1.0,
    0.0,  1.0,  0.0,  1.0,
  ];
  gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(colors), gl.STATIC_DRAW);
  pyramidVertexColorBuffer.itemSize = 4;
  pyramidVertexColorBuffer.numItems = 12;


  cubeVertexPositionBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
  vertices = [
    //Front
    -1.0,  -1.0,  1.0,
    1.0,  -1.0,  1.0,
    1.0,    1.0,  1.0,
    -1.0,   1.0,  1.0,
    //Back
    -1.0,  -1.0,  -1.0,
    -1.0,  1.0,  -1.0,
    1.0,    1.0,  -1.0,
    1.0,   -1.0,  -1.0,
    //Top
    -1.0,  1.0,  -1.0,
    -1.0,  1.0,  1.0,
    1.0,    1.0,  1.0,
    1.0,   1.0,  -1.0,
    //Buttom
    -1.0,  -1.0, -1.0,
    1.0,   -1.0,  -1.0,
    1.0,   -1.0,  1.0,
    -1.0,   -1.0,  1.0,
    //Right
    1.0,  -1.0, -1.0,
    1.0,   1.0, -1.0,
    1.0,   1.0,  1.0,
    1.0,  -1.0,  1.0,
    //Left
    -1.0,  -1.0,  -1.0,
    -1.0,  -1.0,  1.0,
    -1.0,   1.0,  1.0,
    -1.0,   1.0,  -1.0
   ];
  gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(vertices), gl.STATIC_DRAW);
  cubeVertexPositionBuffer.itemSize = 3;
  cubeVertexPositionBuffer.numItems = 24;

  cubeVertexColorBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexColorBuffer);
  colors = [
    [1.0, 0.0,  0.0,  1.0],
    [1.0, 1.0,  0.0,  1.0],
    [0.0, 1.0,  0.0,  1.0],
    [1.0, 0.5,  0.5,  1.0],
    [1.0, 0.0,  1.0,  1.0],
    [0.0, 0.0,  1.0,  1.0],
   ];

  var unpackedColors = [];
  for (var i in colors){
    var color = colors[i];
    for(var j=0; j<4; j++){
      unpackedColors = unpackedColors.concat(color);
    }
  }

  gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(unpackedColors), gl.STATIC_DRAW);
  cubeVertexColorBuffer.itemSize = 4;
  cubeVertexColorBuffer.numItems = 24;

  cubeVertexIndexBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
  var cubeVertexIndices =[
   0,1,2,  0,2,3,
    4,5,6,  4,6,7,
    8,9,10, 8,10,11,
    12,13,14, 12,14,15,
   16,17,18, 16,18,19,
   20,21,22, 20,22,23
    ];

  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new WebGLUnsignedShortArray(cubeVertexIndices),gl.STATIC_DRAW);
  cubeVertexIndexBuffer.itemSize = 1;
  cubeVertexIndexBuffer.numItems = 36;
}

var dim = 10;
var rotateX = 0;
var frame = 0;
var rPyramid = 0;
var rCube = 0;
var a = 0;
var speed = 8;

function drawPyramid(){
  gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexPositionBuffer);
  gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,pyramidVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

  gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexColorBuffer);
  gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, pyramidVertexColorBuffer.itemSize,gl.FLOAT,false,0,0);

  setMatrixUniforms();
  gl.drawArrays(gl.TRIANGLES, 0, pyramidVertexPositionBuffer.numItems);
}
function drawCube(){
  gl.bindBuffer(gl.ARRAY_BUFFER,cubeVertexPositionBuffer);
  gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,cubeVertexPositionBuffer.itemSize, gl.FLOAT, false,0,0);

  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexColorBuffer);
  gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, cubeVertexColorBuffer.itemSize,gl.FLOAT,false,0,0);

  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,cubeVertexIndexBuffer);
  setMatrixUniforms();
  gl.drawElements(gl.TRIANGLES,cubeVertexIndexBuffer.numItems,gl.UNSIGNED_SHORT,0);
}

function drawScene(){

  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  perspective(45,800.0/600.0,0.1,100.0);

  var scale = rangeScale * 0.01; 
  var x = (rangeX-50) * scale;
  var y = (rangeY-50) * scale;
  var z = (rangeZ-50) * scale;
  
  document.getElementById("scaleValue").innerHTML = scale;
  document.getElementById("xValue").innerHTML = x;
  document.getElementById("yValue").innerHTML = y;
  document.getElementById("zValue").innerHTML = z;

  currentRing.x = x;
  currentRing.y = y;
  //currentRing.z = z;
  currentRing.speed = speed;
  //currentRing.rPyramid = rPyramid;
  currentRing.rotateX = rotateX;
  currentRing.dim = dim;

  ringStack.push(currentRing);


  loadIdentity();

  for(i in ringStack){
    var ring = ringStack[i];
  mvPushMatrix();
    mvTranslate([ring.x,ring.y,z-40]);
    var n = 1*(ring.speed*0.5)+1.0;
    var r = ring.dim*0.1*Math.sin(rPyramid*0.05)+ring.dim*0.5+1;
    mvRotate(ring.rotateX*3,[1,0,0]);
    for(var i=0;i<n;i++){
    mvPushMatrix();
      mvTranslate([r*Math.sin(Math.PI*2.0/n*i),r*Math.cos(Math.PI*2.0/n*i),0]);
      mvRotate(rPyramid,[0,0,1]);
      mvRotate(90,[1,0,0]);
      drawPyramid();
    mvPopMatrix();
    }
  mvPopMatrix();
  }

  ringStack.pop();

  
}

var lastTime = 0;

function animate(){
  var nowTime = (new Date).getTime();
  if(lastTime != 0){
    var elapsed = nowTime - lastTime;
    a = 2.0*Math.sin(Math.PI * rPyramid*0.01);
    rPyramid += (90*elapsed) / 1000.0;
    //rCube += (75*elapsed) / 1000.0;
  }
  lastTime = nowTime;
}
function tick(){
  hKeys();
  drawScene();
  animate();
}

var currentlyPressedKey = new Object();
function hKeyDown(event){
  currentlyPressedKey[event.keyCode] = true;
}
function hKeyUp(event){
  currentlyPressedKey[event.keyCode] = false;
  if(event.keyCode == "P".charCodeAt(0)){
    ringStack.push(currentRing);
    currentRing = new Object();
  }
}

function hKeys(){
  if(currentlyPressedKey[37]){
    keyRight--;
  }
  if(currentlyPressedKey[38]){
    keyUp++;
  }
  if(currentlyPressedKey[39]){
    keyRight++;
  }
  if(currentlyPressedKey[40]){
    keyUp--;
  }
  if(currentlyPressedKey["L".charCodeAt(0)]){
    rangeX++;
  }
  if(currentlyPressedKey["H".charCodeAt(0)]){
    rangeX--;
  }
  
  if(currentlyPressedKey["K".charCodeAt(0)]){
    rangeY++;
  }
  if(currentlyPressedKey["J".charCodeAt(0)]){
    rangeY--;
  }
  if(currentlyPressedKey["A".charCodeAt(0)]){
    speed++;
  }
  if(currentlyPressedKey["D".charCodeAt(0)]){
    if(speed>0){
       speed--;
    }
  }
  if(currentlyPressedKey["W".charCodeAt(0)]){
    rotateX++;
  }
  if(currentlyPressedKey["S".charCodeAt(0)]){
    rotateX--;
  }
  if(currentlyPressedKey["Q".charCodeAt(0)]){
    if(dim>1){
      dim--;
    }
  }
  if(currentlyPressedKey["E".charCodeAt(0)]){
    dim++;
  }
}

function hmouseup(e){
  rangeX = ((e.pageX-400.0)/400.0)*3.0;
  //rangeY = (-e.pageY+300.0)/300.0;
  alert("pageX:"+e.pageX +"/rangeX:"+rangeX);
}

function webGLStart(){
  document.onkeydown = hKeyDown;
  document.onkeyup = hKeyUp;
  document.onmouseup = hmouseup;
  
  var canvas = document.getElementById("canvas");
  initGL(canvas);
  initShaders();
  initBuffers();

  gl.clearColor(0.0,0.0,0.0,1.0);

  gl.clearDepth(1.0);

  gl.disable(gl.DEPTH_TEST);
  gl.enable(gl.BLEND);
  //gl.enable(gl.DEPTH_TEST);
  //gl.disable(gl.BLEND);
  //gl.depthFunc(gl.LEQUAL);
  gl.blendFunc(gl.SRC_ALPHA,gl.ONE);
  gl.uniform1f(shaderProgram.alphaUniform,0.5);

  setInterval(tick,16);
}
</script>


</head>

<body onload="webGLStart();">
  <canvas id="canvas" width="800" height="600"></canvas>
  <dl>
    <dt>A,D</dt>
    <dd>数増やす/減らす</dd>
    <dt>H,J,K,L</dt>
    <dd>移動</dd>
    <dt>W,S</dt>
    <dd>傾ける</dd>
    <dt>Q,E</dt>
    <dd>揺れ幅</dd>
    <dt>P</dt>
    <dd>固定</dd>
  </dl>
  
  <dl>
    <dt>rangeScale:<span id="scaleValue"></span></dt>
    <dd><input id="rangeScale" type="range" onmouseup="rangeScale = this.value"/></dd>
    <dt>rangeX:<span id="xValue"></span></dt>
    <dd><input id="rangeX" type="range" onmouseup="rangeX = this.value"/></dd>
    <dt>rangeY:<span id="yValue"></span></dt>
    <dd><input id="rangeY" type="range" onmouseup="rangeY = this.value"/></dd>
    <dt>rangeZ:<span id="zValue"></span></dt>
    <dd><input id="rangeZ" type="range" onmouseup="rangeZ = this.value"/></dd>
</body>

</html>
